package bsserver;

import java.io.IOException;
import java.net.ServerSocket;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * BSServer Main startup class
 * @author marc, tblackstone
 */
public class Main {

    private static ServerSocket sock = null;
    private static int port;
    private static boolean running;
    /**
     * The configuration associated with the server
     */
    public static HttpdConf HttpdConfig;
    /**
     * The list of recognized MIME types and file extensions
     */
    public static MimeTypes MimeType;
    /**
     * The connection logger used by the server
     */
    public static AccessLogger AccessLog;
    private static Double ServerVersion = new Double(2.0);
    private static String ServerName = new String("bsserver");
    private static int threads;

    private Main() { }

    /**
     * Main BSServer entry point
     * @param args the command line arguments
     */
    public static void main(String[] args) {



        // Decode command line
        CommandLineDecoder commandLine = new CommandLineDecoder(args);

        // Load from "httpd.conf" or whatever was specified on command line
        HttpdConfig = new HttpdConf(commandLine.argument("-r"));

        // Load from "MIME.types" or whatever was defined in httpd.conf
        MimeType = new MimeTypes(HttpdConfig.get("TypesConfig"));

        // start logging
        if (commandLine.argument("-l") != null) {
            AccessLog = new AccessLogger(commandLine.argument("-l"));
        } else {
            AccessLog = new AccessLogger(HttpdConfig.get("LogFile"));
        }


        // Set port to listen to
        if (commandLine.argument("-p") != null) {
            port = Integer.parseInt(commandLine.argument("-p"));
        } else {
            port = Integer.parseInt(HttpdConfig.get("Listen"));
        }

        try {
            start();
        } catch (IOException ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }




    }

    /**
     * Starts running main program loop
     * @throws java.io.IOException
     */
    public static void start() throws IOException {

        // Open server socket on specified port
        sock = new ServerSocket(port);
        System.out.println("Server running on port " + port);
        System.out.println();

        running = true;
        threads = 0;
        final int maxThreads = Integer.parseInt(HttpdConfig.get("MaxThreads"));
        
        while (running) {

            while (threads == maxThreads) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException ex) {
                }
            }

            // Start a new thread to handle each connection
            // sock.accept() waits for new connection
            // then continues to ClientConnection().start() when
            // new incoming connection started.
            try {
                new ClientConnection(sock.accept()).start();
                threads++;
            } catch (Exception ex) {
                Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        sock.close();

    }

    /**
     * Stops server from running
     */
    public static void stop() {
        running = false;
    }

    /**
     * Notifies the server that a connection has closed.
     */
    public static void connectionClosed() {
        // Client connection has been closed, decrement thread counter
        if (threads > 0) {
            threads--;
        }
    }

    /**
     * Returns a String containing the server name and version info.
     * @return server name and version info
     */
    public static String getServerInfo() {
        return ServerName + " v" + ServerVersion.toString();
    }
}

/**
 * Command line decoder.
 * @author marc
 */
class CommandLineDecoder {

    private HashMap<String, String> parameters =
            new HashMap<String, String>();
    private static final HashMap<String, String> validParams =
            new HashMap<String, String>();

    /**
     * Must call this method, passing the command line argument strging array.
     * @param args
     */
    public CommandLineDecoder(String[] args) {


        // Set default system paramters (per httpd command line args format)
        parameters.put("-r", "httpd.conf");

        if (args.length == 0) {
            return;
        }

        for (int i = 0; i < args.length; i++) {
            if (validParams.containsKey(args[i])) {
                if (args[i].equals("-h")) {
                    printUsage();
                    System.exit(0);
                }
                if ((i + 1) < args.length) {
                    parameters.put(args[i], args[i + 1]);
                } else {
                    parsingError();
                }
            } else {
                invalidOptionError(args[i]);
            }
        }
    }

    /**
     * Returns the value associated with the specified command line argument.
     * @param arg command line argument
     * @return value specified for arg at command line
     */
    public String argument(String arg) {

        return parameters.get(arg);

    }

    private void invalidOptionError(String option) {
        System.err.println("Error: invalid command line option " + option);
        System.err.println();
        printUsage();
        System.exit(1);
    }

    private void parsingError() {
        System.err.println("Error parsing command line arguments");
        System.err.println();
        printUsage();
        System.exit(1);
    }

    private void printUsage() {
        System.out.println("Usage:");
        for (String optDescription : validParams.values()) {
            System.out.println(optDescription);
        }
        System.out.println();
    }


    static {

        // validParams is a list of command line options and a description of their usage
        validParams.put("-h", " -h\n     Print this help message.");
        validParams.put("-r", " -r rulefile\n     Use rulefile as " +
                "configuration file. Only necessary if you don't have the " +
                "default configuration file httpd.conf");
        validParams.put("-p", " -p port#\n     Listen on port port#. This " +
                "will override any option present in the configuation file.");
        validParams.put("-l", " -l logfile\n     Use logfile to log the " +
                "requests. This will override any option present in the " +
                "configuation file.");

    }
}

